/*
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.
  
    This is sample code and is freely distributable.
*/

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using svs.Mobile.BeyondMedia.FeedCore;
using svs.Mobile.BeyondMedia.FeedCore.Helpers;
using svs.Mobile.BeyondMedia.GraphicsAPI;
using svs.Mobile.BeyondMedia.Objects;
using svs.Mobile.BeyondMedia.Properties;

namespace svs.Mobile.BeyondMedia.Controls
{
	/// <summary>
	/// Provide a 2 line listview
	/// </summary>
	internal class TracksView : OwnerDrawnList<Track>, IView, ITracksView
	{
		private const int Column1Left = 5;
		private const int Column2Left = 25;
		private const int FONT_SIZE = 10;
		private const int IMAGE_HEIGHT = 16;
		private const int IMAGE_WIDTH = 16;
		private int m_AnimationFrame = 0;
		private Feed m_FeedFilter;

		#region Events

		public event Helpers.TrackSelectedEvent Select;
		public event Helpers.NavigationEvent Navigation;

		#endregion

		#region Constructor

		public TracksView()
		{
			// We need a total of 5 rows, so the height is 180/5=36
			using (Graphics g = CreateGraphics())
			{
				using (Font font = new Font(Font.Name, FONT_SIZE, FontStyle.Bold))
				{
					// Calc line height to be height of letter A plus 10%
					int fontHeight = (int) (g.MeasureString("A", font).Height*1.1);
					ItemHeight = fontHeight * 2;
				}
			}

			FeedRepository.TrackDeleted += OnTrackDeleted;
			FeedRepository.TrackAdded += OnTrackAdded;

		}

		#endregion

		#region Private Methods

		private void OnTrackAdded(Track i_track)
		{
			if (InvokeRequired)
			{
				TrackUpdatedDelegate cbDel = OnTrackAdded;
				Invoke(cbDel, i_track);
				return;
			}

			if (IsAllowed(i_track))
			{
				LoadTracks(SelectedTrack);
			}
		}

		private void LoadTracks(Track trackToSelect)
		{
			Items.Clear();

			TrackList tracks = new TrackList();
			tracks.AddRange(FeedRepository.GetAllTracks().FindAll(IsAllowed));

			if (GetFeedFilter() == null) // we show all tracks in the category
				FeedRepository.SortTrackUsingDefaultSettings(tracks);

			Items.AddRange(tracks);

			if (trackToSelect != null)
				SelectedTrack = trackToSelect;
			else
				SelectFirstUnplayedTrack();

			ViewManager.TrySetTitle(Title);

			OnResize(null);
			Refresh();
		}

		private bool IsAllowed(Track track)
		{
			if(track == null ) return false;

			if (m_FeedFilter == null)
				return CategoryManager.IsInActiveCategory(track.ParentFeed);
			else
				return track.ParentFeed == m_FeedFilter;
		}

		private void OnTrackDeleted(Track i_track)
		{
			if (InvokeRequired)
			{
				TrackUpdatedDelegate cbDel = OnTrackDeleted;

				Invoke(cbDel, i_track);
				return;
			}

			if (Items.Contains(i_track))
			{
				Track selected = SelectedTrack;

				Items.Remove(i_track);

				//Deal with the current selection
				if (selected == i_track)
				{
					if (Items.Count > 0)
					{
						if (SelectedIndex >= Items.Count)
							SelectedIndex = Items.Count - 1;
					}
					else
						SelectedIndex = -1;
				}
				else
					SelectedTrack = selected;

				ViewManager.TrySetTitle(Title);
				OnResize(null);
				Refresh();
			}
		}

		private void SelectFirstUnplayedTrack()
		{
			for (int i = 0; i != Items.Count; i++)
			{
				if (((Track) Items[i]).IsNew || ((Track) Items[i]).IsPartialyPlayed)
				{
					SelectedTrack = (Track) Items[i];
					return;
				}
			}

			SelectedIndex = -1;
		}

		// Check if the user presses the action key

		private void OnNavigation(Helpers.NavigationDirection i_direction)
		{
			if (Navigation != null)
				Navigation(this, i_direction);
		}

		private void DrawItemBox(Graphics g, Color lineColor, bool selected, bool touched, Rectangle rc, float percentPlayed)
		{
			if (touched)
			{
				using (Brush br = new SolidBrush(ItemTouchedColor))
				{
					g.FillRectangle(br, rc);
				}
			}
			else if (selected)
			{
				GradientFill.Fill(g, rc, ItemHighLightColor1, ItemHighLightColor2, GradientFill.FillDirection.LeftToRight);
			}

			using (Pen linePen = new Pen(lineColor))
			{
				g.DrawLine(linePen, rc.Left,
				           rc.Bottom,
				           rc.Right,
				           rc.Bottom);
			}

			if (percentPlayed > 0)
			{
				float width = rc.Width;
				width *= percentPlayed;

				Rectangle highlighted = new Rectangle(rc.Left, rc.Top, (int) Math.Floor(width), rc.Height);
				GradientFill.Fill(g, highlighted, selected? ItemHighLightColor1:
																										BackColor,
																					selected? Helpers.ChangeColorIntensity(ItemHighLightColor1, 0.75):
																										Helpers.ChangeColorIntensity(BackColor, 0.75) , GradientFill.FillDirection.LeftToRight);
			}
		}

		private delegate void TrackUpdatedDelegate(Track i_track);

		#endregion

		#region Protected Methods

		protected override void OnKeyDown(KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Return:

					if (SelectedIndex != -1)
					{
						PlaySelected();
					}
					e.Handled = true;
					break;

					//Left Right
				case Keys.Left:
					OnNavigation(Helpers.NavigationDirection.Back);
					e.Handled = true;
					break;

					//Left Right
				case Keys.Right:
					OnNavigation(Helpers.NavigationDirection.Forward);
					e.Handled = true;
					break;
			}

			if (!e.Handled)
			{
				base.OnKeyDown(e);

				if (!e.Handled)
					e.Handled = ShortcutKeyManager.HandleKey(e.KeyCode);
			}
		}

		protected override void ItemClicked(int i_ItemIndex, int X, int Y, DragHelper.ClickSpeed speed)
		{
			base.ItemClicked(i_ItemIndex,X,Y, speed);

			if(speed == DragHelper.ClickSpeed.Fast)
				PlaySelected();
		}

		/// <summary>
		/// Custom OnPaint. This paints the listview items
		/// </summary>
		/// <param name="e"></param>
		protected override void OnPaint(PaintEventArgs e)
		{
			// Declare vars
			Font fontMainItem;
			Font fontNumber;
			Color fontColor;
			Bitmap bmp;

		using (Graphics gOffScreen = PrepareOffscreenGraphics())
		{
			// Set the y pos of the current item
			int itemTop = 0;

			// Draw the visible items.
			for (int n = VScrollBar.Value; n <= VScrollBar.Value + DrawCount; n++)
			{
				if (n > Items.Count - 1)
					break;

				Rectangle itemArea;
				itemArea = new Rectangle(0,
				                         itemTop,
				                         ClientSize.Width - (VScrollBar.Visible ? VScrollBar.Width : 0),
				                         ItemHeight);

				// Get the current MailItem
				Track track = (Track) Items[n];

				if (n == TouchedIndex)
				{
					DrawItemBox(gOffScreen,
											ItemSeparatorColor,
											false,
											true,
											itemArea,
											track.PlayedAsFraction);

					fontColor = ForeColor;
				}
				else if (n == SelectedIndex)
				{
					DrawItemBox(gOffScreen,
					            ItemSeparatorColor,
					            true,
											false,
					            itemArea,
					            track.PlayedAsFraction);

					fontColor = CalcTextColor(SystemColors.MenuText);
				}
				else
				{
					fontColor = ForeColor;
					DrawItemBox(gOffScreen,
					            ItemSeparatorColor,
					            false,
											false,
					            itemArea,
					            track.PlayedAsFraction);
				}

				bmp = Helpers.GetTrackIcon(track);

				fontMainItem = new Font(Font.Name, FONT_SIZE, FontStyle.Regular);

				// Draw the image
				if (track.DownloadStatus == Track.DownloadStatuses.Downloading || !track.IsFullyDownloaded)
				{
					int currentFrame = 0;
					bmp = Resources.downloading;

					m_AnimationFrame++;
					int numFrames = bmp.Width/IMAGE_WIDTH;
					if (m_AnimationFrame >= numFrames)
						m_AnimationFrame = 0;

					currentFrame = m_AnimationFrame;

					ImageAttributes ia = new ImageAttributes();
					ia.SetColorKey(Color.Red, Color.Red);
					Rectangle imgRect = new Rectangle(Column1Left, itemTop + 3, IMAGE_WIDTH, IMAGE_HEIGHT);
					gOffScreen.DrawImage(bmp, imgRect, currentFrame*IMAGE_WIDTH, 0, IMAGE_WIDTH, IMAGE_HEIGHT, GraphicsUnit.Pixel, ia);
				}
				else
				{
					bmp = Helpers.GetTrackIcon(track);
					ImageAttributes ia = new ImageAttributes();
					ia.SetColorKey(Color.White, Color.White);
					Rectangle imgRect = new Rectangle(Column1Left, itemTop + 3, bmp.Width, bmp.Height);
					gOffScreen.DrawImage(bmp, imgRect, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, ia);
				}

				// Draw the name of the track
				Rectangle nameRect = new Rectangle(Column2Left, itemTop, itemArea.Width - Column2Left, ItemHeight);

				string name = track.Name;

				if (Width <= 240 && name.Length > 45)
					name = name.Substring(0, 45) + "...";
				else if (Width > 240 && name.Length > 65)
					name = name.Substring(0, 65) + "...";

				using (Brush b = new SolidBrush(fontColor))
				{
					gOffScreen.DrawString(name,fontMainItem,b, nameRect);
				}

				//Draw the track time 
				itemArea.Inflate(-4, -2);

				StringFormat sf = new StringFormat();
				sf.Alignment = StringAlignment.Far;
				sf.LineAlignment = StringAlignment.Far;

				fontNumber = new Font(Font.Name, FONT_SIZE - 2, FontStyle.Regular);

				if (!track.IsFullyDownloaded)
				{
					string dwInfo;

					if (track.DownloadSize <= 0)
					{
						dwInfo = string.Format("{0} {1}",
						                       track.DownloadStatus,
						                       CoreHelper.GetFileLengthAsString(track.DownloadedPortion));
					}
					else
					{
						dwInfo = string.Format("{0} {1}% of {2}",
						                       track.DownloadStatus,
						                       (int) track.DownloadPercent,
						                       CoreHelper.GetFileLengthAsString(track.DownloadSize));
					}

					using (Brush b = new SolidBrush(fontColor))
					{
						gOffScreen.DrawString(dwInfo,fontNumber,b,itemArea, sf);
					}
				}
				else if (track.TotalTime > 0 && (track.IsPlayed || track.IsPartialyPlayed))
				{
					using (Brush b = new SolidBrush(fontColor))
					{
						gOffScreen.DrawString(string.Format("{0}", track.TotalTimeAsString),fontNumber,b,itemArea, sf);
					}
				}

				// Cleanup
				fontNumber.Dispose();
				fontMainItem.Dispose();
				bmp.Dispose();

				// Set the next item top to move down the item height
				itemTop += ItemHeight + 1;
			}

			// Now draw the visible list box
			e.Graphics.DrawImage(OffScreen, 0, 0);
		}

			base.OnPaint(e);
		}

		/// <summary>
		/// Draw a border around control (if needed)
		/// </summary>
		/// <param name="e"></param>
		protected override void OnPaintBackground(PaintEventArgs e)
		{}

		protected override string GetItemText(int ItemIndex)
		{
			Track t = Items[ItemIndex] as Track;
			if (t != null)
				return t.Name;

			return string.Empty;
		}


		#endregion

		#region Public Properties

		/// <summary>
		/// Gets the current playlist.
		/// </summary>
		/// <value>The current playlist.</value>
		public TrackList CurrentPlaylist
		{
			get
			{
				TrackList retVal = new TrackList();
				for (int i = 0; i != Items.Count; i++)
				{
					retVal.Add((Track)Items[i]);
				}
				return retVal;
			}
		}

		public Track SelectedTrack
		{
			get
			{
				if (SelectedIndex >= 0 && SelectedIndex < Items.Count)
					return Items[SelectedIndex] as Track;
				else
					return null;
			}

			set
			{
				SelectedIndex = Items.IndexOf(value);
				EnsureVisible(SelectedIndex);
			}
		}

		/// <summary>
		/// Gets the title.
		/// </summary>
		/// <value>The title.</value>
		public string Title
		{
			get
			{
				if (m_FeedFilter == null)
					return string.Format("{0} Podcasts ({1})",Configuration.ActiveFeedCategory.Name, Items.Count);
				else
					return string.Format("{0} ({1})",m_FeedFilter.Name,Items.Count);
			}
		}

		/// <summary>
		/// Gets the view menu.
		/// </summary>
		/// <value>The view menu.</value>
		public MainMenu ViewMenu
		{
			get { return MenuManager.GetMenu(Helpers.ViewNames.Tracks); }
		}

		#endregion

		#region Public Methods

		public void Initialize()
		{
			Items.Clear();
			Items.AddRange(FeedRepository.GetAllTracks().FindAll(IsAllowed));
		}

		public void OnBeforeActivate()
		{
		}

		/// <summary>
		/// Gets a screenshot of the view for transitions
		/// </summary>
		/// <returns></returns>
		public Bitmap CreateScreenShot()
		{
			return GraphicsHelper.CreateControlSnapshot(this);
		}

		public void OnAfterDeactivate()
		{
			
		}

		public void OnAfterActivate()
		{
			Focus();
		}

		/// <summary>
		/// Plays the selected.
		/// </summary>
		public void PlaySelected()
		{
			if (SelectedTrack == null)
			{
				Helpers.Beep();
				return;
			}

			if (Select != null)
				Select(SelectedTrack);
		}

		/// <summary>
		/// Clears this instance.
		/// </summary>
		public void Clear()
		{
			m_FeedFilter = null;
			Items.Clear();
			SelectedIndex = -1;
		}

		public void Reload()
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new ReloadDelegate(Reload));
				return;
			}

			LoadTracks(SelectedTrack);
		}

		private delegate void ReloadDelegate();

		/// <summary>
		/// True if the view is showing data for a given feed
		/// </summary>
		/// <param name="feed"></param>
		/// <returns></returns>
		public bool IsShowingFeed(Feed feed)
		{
			return !HasFeedFilter() || m_FeedFilter == feed;
		}

		public void ClearFeedFilter()
		{
			SetFeedFilter(null);
		}

		public Feed GetFeedFilter()
		{
			return m_FeedFilter;
		}

		public void SetFeedFilter(Feed feed)
		{
			if (m_FeedFilter != feed)
			{
				m_FeedFilter = feed;
				LoadTracks(null);
			}
		}

		public bool HasFeedFilter()
		{
			return m_FeedFilter != null;
		}

		public void SelectNextTrack()
		{
			if (SelectedIndex < Items.Count - 1)
			{
				SelectedTrack = Items[SelectedIndex + 1];
			}
		}

		public void NotifyTrackStatusChanged(Track track)
		{
			Refresh();
		}

		#endregion
	}
}